Le complément à 2 est la méthode standard pour représenter les entiers relatifs. Le bit de poids fort (le plus à gauche) indique le signe (0 pour positif, 1 for négatif).
Exemple pour -5 sur 4 bits : 5 $\rightarrow$ 0101 $\rightarrow$ (inversion) 1010 $\rightarrow$ (ajout de 1) 1011. Donc $(-5)_{10} = (1011)_2$.
Exemple pour 1011 sur 4 bits : 1011 $\rightarrow$ (inversion) 0100 $\rightarrow$ (ajout de 1) 0101. $(0101)_2 = 5_{10}$. Le nombre est donc -5.
La représentation des nombres réels en machine est souvent une approximation. Un nombre binaire à virgule se décompose avec des puissances de 2, y compris négatives pour la partie fractionnaire.
Certains nombres décimaux à développement fini n'ont pas de représentation binaire finie (ex: $0.1_{10} = 0.000110011..._2$). Cela mène à des erreurs d'arrondi inévitables.
>>> 0.1 + 0.2 0.30000000000000004
Recommandation : Pour comparer deux flottants `a` et `b`, on ne teste jamais `a == b`. On vérifie si leur différence est inférieure à une petite marge d'erreur (epsilon) : `abs(a - b) < epsilon`.
Un nombre est décomposé en 3 parties (norme IEEE-754) : signe (1 bit), exposant (décale la virgule) et mantisse (les chiffres significatifs). Cela permet de représenter des nombres très grands ou très petits.
Un ordinateur ne manipulant que des nombres, chaque caractère (lettre, symbole, etc.) doit être associé à un nombre unique. C'est le rôle de la table d'encodage.
coordonnees = (10.5, 4.2)
def division_euclidienne(a, b): quotient = a // b reste = a % b return (quotient, reste) # Retourne un tuple q, r = division_euclidienne(10, 3) # q=3, r=1
personne = {"nom": "Alice", "age": 25}
personne["age"]
renvoie `25`.personne["ville"] = "Paris"
for cle, valeur in personne.items(): print(f"{cle}: {valeur}")
tableau[0]
est le premier élément, `tableau[-1]` est le dernier.tableau[1] = "nouvelle_valeur"
.tableau.append("dernier")
.tableau.pop(2)
(supprime l'élément à l'index 2).carres = [i*i for i in range(10)]
matrice = [[1, 2], [3, 4]]
matrice[ligne][colonne]
. Ainsi, matrice[1][0]
vaut `3`.Le parcours séquentiel consiste à examiner chaque élément d'une structure de données (comme un tableau) l'un après l'autre, du début à la fin. C'est l'approche la plus simple mais elle est fondamentale.
Recherche l'index de la première apparition d'une valeur dans un tableau.
def recherche_occurrence(tableau, valeur): for i in range(len(tableau)): if tableau[i] == valeur: return i # Retourne l'index si la valeur est trouvée return -1 # Convention pour indiquer que la valeur n'est pas dans le tableau
def recherche_extremum(tableau): if not tableau: # Cas d'un tableau vide return (None, None) minimum = tableau[0] maximum = tableau[0] for element in tableau: if element < minimum: minimum = element if element > maximum: maximum = element return (minimum, maximum)
def calcul_moyenne(tableau): if not tableau: return 0 somme = 0 for element in tableau: somme += element return somme / len(tableau)
Le but du tri est d'organiser les éléments d'un tableau dans un ordre spécifique (généralement croissant).
Principe : On trouve le plus petit élément du tableau non trié et on l'échange avec l'élément au début de la partie non triée. On répète l'opération jusqu'à ce que tout le tableau soit trié.
def tri_selection(tableau): n = len(tableau) for i in range(n): # Trouver le minimum dans la partie non triée tableau[i..n-1] min_idx = i for j in range(i + 1, n): if tableau[j] < tableau[min_idx]: min_idx = j # Échanger le minimum trouvé avec le premier élément tableau[i], tableau[min_idx] = tableau[min_idx], tableau[i] return tableau
Principe : On parcourt le tableau et pour chaque élément, on l'insère à sa place correcte dans la partie déjà triée (qui se trouve à sa gauche).
def tri_insertion(tableau): for i in range(1, len(tableau)): cle = tableau[i] j = i - 1 # Déplacer les éléments de tableau[0..i-1] qui sont plus grands que la clé # vers une position à droite de leur position actuelle while j >= 0 and cle < tableau[j]: tableau[j + 1] = tableau[j] j -= 1 tableau[j + 1] = cle return tableau
Pour classer un nouvel élément, on regarde ses k voisins les plus proches dans un jeu de données existant. Le nouvel élément se voit attribuer la classe la plus fréquente parmi ces k voisins.
import math def distance_euclidienne(point1, point2): # Suppose que les points sont des tuples/listes de coordonnées (x, y) return math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2) def k_plus_proches_voisins(donnees, point_a_classer, k): distances = [] for categorie, points_categorie in donnees.items(): for point in points_categorie: dist = distance_euclidienne(point, point_a_classer) distances.append((dist, categorie)) # Trier les distances par ordre croissant distances.sort(key=lambda x: x[0]) # Prendre les k premiers voisins voisins = distances[:k] # Compter les votes votes = {} for _, categorie in voisins: votes[categorie] = votes.get(categorie, 0) + 1 # Retourner la catégorie avec le plus de votes return max(votes, key=votes.get)
C'est une méthode de recherche très efficace qui consiste à diviser récursivement par deux l'intervalle de recherche. On compare l'élément cherché avec l'élément central du tableau. Si ce n'est pas le bon, on sait si l'élément se trouve dans la moitié gauche ou droite, et on élimine l'autre moitié.
def recherche_dichotomique(tableau, valeur): debut = 0 fin = len(tableau) - 1 while debut <= fin: milieu = (debut + fin) // 2 if tableau[milieu] == valeur: return milieu # Trouvé ! elif tableau[milieu] < valeur: debut = milieu + 1 # Chercher dans la moitié droite else: # tableau[milieu] > valeur fin = milieu - 1 # Chercher dans la moitié gauche return -1 # Non trouvé
Un algorithme glouton (greedy algorithm) construit une solution à un problème étape par étape. À chaque étape, il fait le choix qui semble le meilleur localement, sans tenir compte des conséquences futures de ce choix.
Problème : Rendre une somme donnée avec le moins de pièces/billets possible. Stratégie gloutonne : Toujours donner la plus grande pièce/billet possible sans dépasser la somme restante.
def rendre_monnaie(montant_a_rendre, systeme_monetaire): # systeme_monetaire doit être trié par ordre décroissant # ex: [50, 20, 10, 5, 2, 1] rendu = {} for piece in systeme_monetaire: if montant_a_rendre >= piece: nombre_pieces = montant_a_rendre // piece rendu[piece] = nombre_pieces montant_a_rendre %= piece return rendu # Pour le système euro, cette stratégie est optimale. # Ce n'est pas vrai pour tous les systèmes monétaires !
Problème : Remplir un sac de capacité limitée avec des objets ayant chacun une valeur et un poids pour maximiser la valeur totale. Stratégie gloutonne (une parmi d'autres) : Trier les objets par rapport valeur/poids décroissant et prendre les meilleurs objets jusqu'à ce que le sac soit plein. Cette stratégie n'est pas toujours optimale pour le problème du sac à dos 0/1 (où on doit prendre ou laisser un objet entier).